/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.polimi.meteoCal.presentation;

import it.polimi.meteoCal.boundary.BadWeatherNotificationFacade;
import it.polimi.meteoCal.boundary.CalendarFacade;
import it.polimi.meteoCal.boundary.EventFacade;
import it.polimi.meteoCal.boundary.RequestFacade;
import it.polimi.meteoCal.boundary.SuggestionFacade;
import it.polimi.meteoCal.boundary.UpdatesFacade;
import it.polimi.meteoCal.boundary.UsersFacade;
import it.polimi.meteoCal.boundary.WeatherForecastFacade;
import it.polimi.meteoCal.control.NotificationManager;
import it.polimi.meteoCal.control.WeatherManager;
import it.polimi.meteoCal.control.util.DateUtil;
import it.polimi.meteoCal.entity.BadWeatherNotification;
import it.polimi.meteoCal.entity.Event;
import it.polimi.meteoCal.entity.PrecipitationType;
import it.polimi.meteoCal.entity.Request;
import it.polimi.meteoCal.entity.Suggestion;
import it.polimi.meteoCal.entity.Updates;
import it.polimi.meteoCal.entity.Users;
import it.polimi.meteoCal.entity.WeatherForecast;
import it.polimi.meteoCal.presentation.util.MessageUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.primefaces.event.FlowEvent;

/**
 *
 * @author Michele
 */
@Named(value = "eventView")
@SessionScoped
public class EventView implements Serializable {

    @Inject
    private UsersView usersView;
    @Inject
    private NotificationManager notificationManager;
    @EJB
    private UsersFacade usersFacade;
    @EJB
    private EventFacade eventFacade;
    @EJB
    private WeatherForecastFacade weatherForecastFacade;
    @EJB
    private CalendarFacade calendarFacade;
    @EJB
    private RequestFacade requestFacade;
    @EJB
    private UpdatesFacade updateFacade;
    @EJB
    private BadWeatherNotificationFacade bwnFacade;
    @EJB
    private SuggestionFacade suggestionFacade;

    private boolean visibleCustom = true;
    private boolean visibleWeather = true;

    private Event event;
    private Event nextEvent;

    private boolean boolPrecipitationType;
    private boolean boolWindSpeed;
    private boolean boolTemperature;
    private boolean boolCloudsCoverage;

    private List<Users> invitable;
    private List<Users> invitated;

    private boolean creator;
    private boolean mineOrCreator;

    private String precipitationTypeTemp;

    public String getPrecipitationTypeTemp() {
        return precipitationTypeTemp;
    }

    public void setPrecipitationTypeTemp(String precipitationTypeTemp) {
        this.precipitationTypeTemp = precipitationTypeTemp;
    }

    public boolean isVisibleCustom() {
        String cus = "custom";
        return cus.equals(event.getWeatherSpescs());
    }

    public void setVisibleCustom(boolean visibleCustom) {
        this.visibleCustom = visibleCustom;
    }

    public boolean isVisibleWeather() {
        return event.isIsOutdoor();
    }

    public void setVisibleWeather(boolean visibleWeather) {
        this.visibleWeather = visibleWeather;
    }

    /**
     *
     * @param event the specific temp event
     */
    public void setEvent(Event event) {
        this.event = event;
    }

    /**
     *
     * @return the specific temp event or a new one
     */
    public Event getEvent() {
        if (event == null) {
            event = new Event();
        }
        return event;
    }

    public Event getNextEvent() {
        loadNextEvent();
        return nextEvent;
    }

    public void setNextEvent(Event nextEvent) {
        this.nextEvent = nextEvent;
    }

    public List<Users> getInvitable() {
        invitable = usersFacade.findAll();
        if (!eventFacade.getGoingUsers(event).isEmpty()) {
            for (it.polimi.meteoCal.entity.Calendar c : eventFacade.getGoingUsers(event)) {
                if (invitable.contains(c.getUser())) {
                    invitable.remove(c.getUser());
                }
            }
        }
        return invitable;
    }

    public void setInvitable(List<Users> invitable) {
        this.invitable = invitable;
    }

    public List<Users> getInvitated() {
        if (invitated == null) {
            invitated = new ArrayList<>();
            invitated = eventFacade.getInvitedUser(this.getEvent());
        }
        return invitated;
    }

    public void setInvitated(List<String> invitated) {
        this.invitated.clear();
        for (String s : invitated) {
            for (Users u : this.invitable) {
                if (s.equals(u.getUsername())) {
                    this.getInvitated().add(u);
                }
            }
        }
    }

    public boolean isBoolPrecipitationType() {
        return event.getBadWeatherSpecification().getWeather().isBoolPrecipitationType() & isVisibleCustom();
    }

    public void setBoolPrecipitationType(boolean boolPrecipitationType) {
        event.getBadWeatherSpecification().getWeather().setBoolPrecipitationType(boolPrecipitationType);
    }

    public boolean isBoolWindSpeed() {
        return event.getBadWeatherSpecification().getWeather().isBoolwindSpeed() & isVisibleCustom();
    }

    public void setBoolWindSpeed(boolean boolWindSpeed) {
        event.getBadWeatherSpecification().getWeather().setBoolwindSpeed(boolWindSpeed);
    }

    public boolean isBoolTemperature() {
        return event.getBadWeatherSpecification().getWeather().isBoolTemperature() & isVisibleCustom();
    }

    public void setBoolTemperature(boolean boolTemperature) {
        event.getBadWeatherSpecification().getWeather().setBoolTemperature(boolTemperature);
    }

    public boolean isBoolCloudsCoverage() {
        return event.getBadWeatherSpecification().getWeather().isBoolCloudsCoverage() & isVisibleCustom();
    }

    public void setBoolCloudsCoverage(boolean boolCloudsCoverage) {
        event.getBadWeatherSpecification().getWeather().setBoolCloudsCoverage(boolCloudsCoverage);
    }

    /**
     * Load the next event for the logged user
     */
    public void loadNextEvent() {
        nextEvent = null;
        List<Event> userGoingEventsList = calendarFacade.getGoingEvents(usersView.getLoggedUser().getCalendar());
        if (!userGoingEventsList.isEmpty()) {
            for (Event e : userGoingEventsList) {
                if (isFutureEvent(e)) {
                    nextEvent = e;
                    break;
                }
            }

            //Event nextEvent = userGoingEventsList.iterator().next();
            for (Event e : userGoingEventsList) {
                if (nextEvent != null) {
                    if (e.getStartDate().before(nextEvent.getStartDate())
                            && isFutureEvent(e)) {
                        nextEvent = e;
                    }
                }
            }
            /*
             if (isFutureEvent(nextEvent)) {
             this.nextEvent = nextEvent;
             } else {
             this.nextEvent = null;
             }*/
        }
    }

    private boolean isFutureEvent(Event e) {
        Calendar now = DateUtil.today();
        return now.get(Calendar.DAY_OF_YEAR) == (DateUtil.dateToCalendar(e.getStartDate()).get(Calendar.DAY_OF_YEAR)) || now.before(DateUtil.dateToCalendar(e.getStartDate()));
    }

    public String onFlowProcess(FlowEvent event) {
        return event.getNewStep();
    }

    /**
     *
     * @return the event wizard start page
     */
    public String startWizard() {
        return "/users/eventWizard.xhtml?faces-redirect=true";
    }

    /**
     * Start wizard from user's home
     *
     * @return the event wizard page
     */
    public String startWizardHome() {
        event = new Event();
        return "/users/eventWizard.xhtml?faces-redirect=true";
    }

    public String editEvent(Event event) {
        this.event = event;
        return startWizard();
    }

    /**
     * Permoforms the creation of an event using the eventFacade and notifies
     * the calendarFacade
     *
     * @return the user's calendar page is reloaded
     */
    public String save() {
        //Remove all previous forecast
        weatherForecastFacade.removeAllByEvent(event);
        List<Users> oldInvited = new ArrayList<>();
        oldInvited.addAll(eventFacade.getInvitedUser(event));
        //Set automatically weather and creator attributes
        eventSetter();
        //Creating the requests for invitated users
        notificationManager.createRequestsForInvitedUser(invitated, oldInvited, event);

        if (event.getId() == null) {
            event.addGoingUser(usersView.getLoggedUser().getCalendar());
            usersView.getLoggedUser().getCalendar().addEvent(event);
            //Observer
            calendarFacade.notifyCreation(event);
            // Persist the event
            eventFacade.create(event);
            // Update the loggedUser's calendar
            calendarFacade.edit(usersView.getLoggedUser().getCalendar());
            // Update all invited users
            usersFacade.edit(invitated);
            MessageUtil.addMessage(null, "Event created");
        } else {
            notificationManager.createUpdateForGoingUser(event);
            //Observer
            calendarFacade.notifyUpdate(event);
            // Update the event, deleting old forecast
            eventFacade.edit(event);
            // Update the loggedUser's calendar ->> BISOGNA AGGIORNARE TUTTI I CALENDARI DEI GOING USERS
            calendarFacade.edit(usersView.getLoggedUser().getCalendar());
            // Update all invited users
            if (invitated != null) {
                usersFacade.edit(invitated);
            }
            MessageUtil.addMessage(null, "Event modified");
        }
        eventReset();
        return "/users/calendar?faces-redirect=true";
    }

    /**
     * Performs the removing of an event using eventFacade and notifies the
     * calendarFacade
     *
     * @return the user's calendar page is reloaded
     */
    public String remove() {
        // Observer
        calendarFacade.notifyRemoving(event);
        // Remove the loggedUser from the event
        event.removeGoingUser(usersView.getLoggedUser().getCalendar());
        // Remove the event from the loggedUser
        usersView.getLoggedUser().getCalendar().removeEvent(event);

        //System.out.println("Invited Users: " + eventFacade.getInvitedUser(event));
        //System.out.println("Going Users: " + eventFacade.getGoingUsers(event));
        if (event.getCreatedBy().equals(usersView.getLoggedUser())) {
            //Remove all bad weather notification associated to the event
            notificationManager.removeBWNForGoingUser(eventFacade.getRequestedUser(event), event);
            //Remove all updates notification associated to the event
            notificationManager.removeUpdateForGoingUser(eventFacade.getRequestedUser(event), event);
            //Remove all request associated to the event
            notificationManager.removeRequestForInvitedUser(eventFacade.getRequestedUser(event), event);
            // Remove all suggestion associated to the event
            notificationManager.removeSuggestionForCreator(event);
            notificationManager.removeAllByEvent(event);
            //update the loggedUser
            usersFacade.edit(usersView.getLoggedUser());
            // Remove the event
            eventFacade.remove(event);
        } else {
            if (usersView.getLoggedUser().getBadWeatherNotification(event) != null) {
                usersView.getLoggedUser().removeBadWeatherNotification(usersView.getLoggedUser().getBadWeatherNotification(event));
            }
            if (usersView.getLoggedUser().getUpdate(event) != null) {
                usersView.getLoggedUser().removeUpdate(usersView.getLoggedUser().getUpdate(event));
            }
            usersFacade.edit(usersView.getLoggedUser());
            // Update the event
            eventFacade.edit(event);
        }
        // Update the loggedUser's calendar
        calendarFacade.edit(usersView.getLoggedUser().getCalendar());
        if (!eventFacade.getGoingUsers(event).isEmpty()) {
            for (it.polimi.meteoCal.entity.Calendar c : eventFacade.getGoingUsers(event)) {
                calendarFacade.edit(c);
            }
        }
        eventReset();
        MessageUtil.addMessage(null, "Event deleted");
        return "/users/calendar?faces-redirect=true";
    }

    public String accept(Request req) {
        event = req.getEvent();
        usersView.getLoggedUser().getCalendar().addEvent(event);
        event.addGoingUser(usersView.getLoggedUser().getCalendar());
        req.setIsRead(true);
        req.setAccepted(true);
        //Observer
        calendarFacade.notifyCreation(event);
        requestFacade.edit(req);
        // Update the event
        eventFacade.edit(event);
        // Update the loggedUser's calendar
        calendarFacade.edit(usersView.getLoggedUser().getCalendar());
        if (!eventFacade.getGoingUsers(event).isEmpty()) {
            for (it.polimi.meteoCal.entity.Calendar c : eventFacade.getGoingUsers(event)) {
                calendarFacade.edit(c);
            }
        }
        eventReset();
        return "/users/calendar?faces-redirect=true";
    }

    public String refuse(Request req) {
        req.setAccepted(false);
        req.setIsRead(true);
        requestFacade.edit(req);
        eventReset();
        return "/users/notification.xhtml?faces-redirect=true";
    }

    public String acceptSuggestion(Suggestion sug) {
        event = sug.getEvent();
        event.setStartDate(sug.getStartDate());
        event.setEndDate(sug.getEndDate());
        sug.setIsRead(true);
        sug.setAccepted(true);
        suggestionFacade.edit(sug);
        return save();
    }

    public String refuseSuggestion(Suggestion sug) {
        sug.setAccepted(false);
        sug.setIsRead(true);
        suggestionFacade.edit(sug);
        eventReset();
        return "/users/notification.xhtml?faces-redirect=true";
    }

    private void eventSetter() {
        event.setAllDay(false);
        if (!event.isIsOutdoor()) {
            event.setWeatherSpescs("off");
        }
        if (event.isIsOutdoor() && !event.getWeatherSpescs().equals("off")) {
            event.setWeatherForecastList(WeatherManager.retrieveForecast(event.getPlace(), event.getStartDate(), event.getEndDate()));
        } else {
            event.getWeatherForecastList().clear();
        }
        event.setCreatedBy(usersView.getLoggedUser());
        for (WeatherForecast f : event.getWeatherForecastList()) {
            f.setEvent(event);
        }
        if (event.getWeatherSpescs().equals("standard")) {
            //Set default bad weather specification in event
            event.getBadWeatherSpecification().getWeather().setBoolCloudsCoverage(false);
            event.getBadWeatherSpecification().getWeather().setBoolPrecipitationType(true);
            event.getBadWeatherSpecification().getWeather().setBoolTemperature(false);
            event.getBadWeatherSpecification().getWeather().setBoolwindSpeed(false);
            event.getBadWeatherSpecification().getWeather().setPrecipitationType(PrecipitationType.Rain);
            event.getBadWeatherSpecification().getWeather().setCloudsCoverage(0);
            event.getBadWeatherSpecification().getWeather().setTemperature(0);
            event.getBadWeatherSpecification().getWeather().setWindSpeed(0);
        } else if (event.getWeatherSpescs().equals("custom") && event.getBadWeatherSpecification().getWeather().isBoolPrecipitationType() && precipitationTypeTemp != null) {
            event.getBadWeatherSpecification().getWeather().setPrecipitationType(PrecipitationType.valueOf(precipitationTypeTemp));
        }
    }

    private void eventReset() {
        event = null;
        boolPrecipitationType = false;
        boolWindSpeed = false;
        boolTemperature = false;
        boolCloudsCoverage = false;

        precipitationTypeTemp = null;

        invitable = null;
        invitated = null;
    }

    public boolean isCreator() {
        return event.getCreatedBy().equals(usersView.getLoggedUser());
    }

    public boolean isCreator(Event event) {
        return event.getCreatedBy().equals(usersView.getLoggedUser());
    }

    public boolean isMineOrCreator() {
        return event.getGoingUsers().contains(usersView.getLoggedUser().getCalendar()) || event.getCreatedBy().equals(usersView.getLoggedUser());
    }

    public String changeParticipationUpdate(Updates update) {
        event = update.getEvent();
        update.setIsRead(true);
        updateFacade.edit(update);
        return remove();
    }

    public String setReadUpdate(Updates update) {
        update.setIsRead(true);
        updateFacade.edit(update);
        return "/users/notification.xhtml?faces-redirect=true";
    }

    public String changeParticipation(BadWeatherNotification bwn) {
        event = bwn.getEvent();
        bwn.setIsRead(true);
        bwnFacade.edit(bwn);
        return remove();
    }

    public String setRead(BadWeatherNotification bwn) {
        bwn.setIsRead(true);
        bwnFacade.edit(bwn);
        return "/users/notification.xhtml?faces-redirect=true";

    }
}
